﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Text.RegularExpressions;

namespace HtmlReader
{
    public class HtmlReader<T> where T : new()
    {
        private readonly Type _type;

        private readonly HtmlTag _tag;
        private readonly KeyValuePair<PropertyInfo, TagContent> _content;
        private readonly List<KeyValuePair<PropertyInfo, TagAttribute>> _attributes;

        public HtmlReader()
        {
            _type = new T().GetType();
            _attributes = new List<KeyValuePair<PropertyInfo, TagAttribute>>();

            // set tag name
            foreach (var attr in
                Attribute.GetCustomAttributes(_type).OfType<HtmlTag>())
            {
                _tag = attr;
            }

            // set attributes name
            foreach (var i in (
                 from property in _type.GetProperties().Where(property => property.CanWrite)
                 from attr in property.GetCustomAttributes(true).OfType<TagAttribute>()
                 select new KeyValuePair<PropertyInfo, TagAttribute>(property, attr)
                ))
            {
                _attributes.Add(i);
            }

            // set content 
            foreach (var i in (
                from property in _type.GetProperties().Where(property => property.CanWrite)
                from attr in property.GetCustomAttributes(true).OfType<TagContent>()
                select new KeyValuePair<PropertyInfo, TagContent>(property, attr)
                ))
            {
                _content = i;
            }
        }

        public IList<T> Read(string source)
        {
            var list = new List<T>();

            // get all {_name} tags <{0}.[^>]*?>.[^<]*?</{0}>|<{0}.[^>]*> 
            var tags = Regex.Matches(source, string.Format(@"(<{0}.[^>]*?>.*?</{0}>|<{0}.[^>]*/?>)", _tag.Name),
                RegexOptions.IgnoreCase | RegexOptions.Singleline);

            foreach (Match tag in tags)
            {
                var value = tag.Groups[1].Value;
                var item = new T();

                // get all attributes for current tag
                foreach (var a in _attributes)
                {
                    var attr = Regex.Match(value, string.Format(@"{0}=[\""|'](.*?)[\""|']", 
                        !string.IsNullOrEmpty(a.Value.Name) ? a.Value.Name : a.Key.Name
                        ),
                        RegexOptions.IgnoreCase | RegexOptions.Singleline);

                    if (attr.Success)
                    {
                        a.Key.SetValue(item, attr.Groups[1].Value, null);
                    }
                }

                // get tag content
                if (_content.Key != null)
                {
                    Regex trim = new Regex(@"\s+", RegexOptions.Compiled);

                    if (_content.Value.TextOnly)
                        _content.Key.SetValue(item, trim.Replace(Regex.Replace(value, @"\s*<.*?>\s*", " ",
                            RegexOptions.Singleline | RegexOptions.Compiled), " ").Trim(), null);
                    else
                        _content.Key.SetValue(item, Regex.Replace(value, string.Format(@"\s*<{0}.*?>|</{0}>\s*", _tag.Name), string.Empty,
                            RegexOptions.Singleline | RegexOptions.IgnoreCase ).Trim(), null);

                }

                list.Add(item);
            }
            return list;
        }
    }
}
